/*
 *      Engine.cpp
 *      
 *      Copyright 2009 Daniel Penney <bikedude880@paqsario.tastywaffle.org>
 * 		Copyright 2005 Anders "Sion" Nissen
 *      
 *      This program is free software; you can redistribute it and/or modify
 *      it under the terms of the GNU General Public License as published by
 *      the Free Software Foundation; either version 2 of the License, or
 *      (at your option) any later version.
 *      
 *      This program is distributed in the hope that it will be useful,
 *      but WITHOUT ANY WARRANTY; without even the implied warranty of
 *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *      GNU General Public License for more details.
 *      
 *      You should have received a copy of the GNU General Public License
 *      along with this program; if not, write to the Free Software
 *      Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 *      MA 02110-1301, USA.
 */

#include "Engine.h"

/** Default constructor. **/
CEngine::CEngine()
{
	m_lLastTick		= 0;
	m_iWidth		= 800;
	m_iHeight		= 600;

	m_pScreen		= 0;
	m_pFPSDisp		= 0;

	m_iFPSTickCounter	= 0;
	m_iFPSCounter		= 0;
	m_iCurrentFPS		= 0;
	m_bFPStoggle		= false;
	m_sFPStext			= new char[16];
}

/** Destructor. **/
CEngine::~CEngine()
{
	TTF_CloseFont( m_pFPSFont );

	TTF_Quit();
	SDL_Quit();
}

/** Sets the height and width of the window.
	@param iWidth The width of the window
	@param iHeight The height of the window
**/
void CEngine::SetSize(const int& iWidth, const int& iHeight)
{
	m_iWidth  = iWidth;
	m_iHeight = iHeight;
	m_pScreen = SDL_SetVideoMode( iWidth, iHeight, 32, SDL_SWSURFACE | SDL_DOUBLEBUF );
}

/** Initialize SDL, the window and the additional data. **/
void CEngine::Init()
{
	cout << "Initializing SDL...	";
	atexit( SDL_Quit );

	if ( SDL_Init( SDL_INIT_VIDEO ) < 0 ) {
		cout << "[FAIL]" << endl;
		cout << "Unable to init SDL: " << SDL_GetError() << endl;
		exit( 1 );
	}

	SetSize( m_iWidth, m_iHeight );

	if ( m_pScreen == NULL ) {
		cout << "[FAIL]" << endl;
		cout << "Unable to set up video: " << SDL_GetError() << endl;
		exit( 1 );
	}
	
	if( TTF_Init() < 0 ) {
        cout << "[FAIL]" << endl;
		cout << "Unable to initialize SDL_ttf: " << SDL_GetError() << endl;
		exit( 1 );   
    }
   
    m_pFPSFont = TTF_OpenFont( "Res/VeraMono.ttf", 10 );

	cout << "[ OK ]" << endl;
	AdditionalInit();
}

/** The main loop. **/
void CEngine::Start()
{
	m_lLastTick = SDL_GetTicks();
	m_bQuit = false;

	// Main loop: loop forever.
	while ( !m_bQuit )
	{
		// Handle mouse and keyboard input
		HandleInput();

        // Do some thinking
        DoThink();

        // Render stuff
        DoRender();
	}

	End();
}

/** Handles all controller inputs.
	@remark This function is called once per frame.
**/
void CEngine::HandleInput()
{
	// Poll for events, and handle the ones we care about.
	SDL_Event event;
	while ( SDL_PollEvent( &event ) )
	{
		switch ( event.type )
		{
		case SDL_KEYDOWN:
			// If escape is pressed set the Quit-flag
			if (event.key.keysym.sym == SDLK_ESCAPE)
			{
				m_bQuit = true;
				break;
			}
			if (event.key.keysym.sym == SDLK_F1)
			{
				if( m_bFPStoggle )
					m_bFPStoggle = false;
				else
					m_bFPStoggle = true;

				break;
			}
			
			KeyDown( event.key.keysym.sym );
			break;

		case SDL_KEYUP:
			KeyUp( event.key.keysym.sym );
			break;

		case SDL_QUIT:
			m_bQuit = true;
			break;
		} // switch
	} // while (handling input)
}

void CEngine::DisplayFPS()
{
	SDL_Rect offset;
	SDL_Color tColor = { 255, 0, 0 };

	sprintf( m_sFPStext, "FPS: %d", GetFPS() );
	m_pFPSDisp = TTF_RenderText_Solid( m_pFPSFont, m_sFPStext, tColor );
	offset.x = (m_iWidth - 60);
	offset.y = 10;

    SDL_BlitSurface( m_pFPSDisp, NULL, m_pScreen, &offset );
}

/** Handles the updating routine. **/
void CEngine::DoThink()
{
	long iElapsedTicks = SDL_GetTicks() - m_lLastTick;
	m_lLastTick = SDL_GetTicks();

	Think( iElapsedTicks );

	m_iFPSTickCounter += iElapsedTicks;
}

/** Handles the rendering and FPS calculations. **/
void CEngine::DoRender()
{
	++m_iFPSCounter;
	if ( m_iFPSTickCounter >= 1000 )
	{
		m_iCurrentFPS = m_iFPSCounter;
		m_iFPSCounter = 0;
		m_iFPSTickCounter = 0;
	}

	SDL_FillRect( m_pScreen, 0, SDL_MapRGB( m_pScreen->format, 0, 0, 0 ) );

	// Lock surface if needed
	if ( SDL_MUSTLOCK( m_pScreen ) )
		if ( SDL_LockSurface( m_pScreen ) < 0 )
			return;

	Render( GetSurface() );

	if( m_bFPStoggle )
		DisplayFPS();

	// Unlock if needed
	if ( SDL_MUSTLOCK( m_pScreen ) )
		SDL_UnlockSurface( m_pScreen );

	// Tell SDL to update the whole gScreen
	SDL_Flip( m_pScreen );
	SDL_Delay(1);
}

/** Retrieve the main screen surface.
	@return A pointer to the SDL_Surface surface
	@remark The surface is not validated internally.
**/
SDL_Surface* CEngine::GetSurface()
{
	return m_pScreen;
}

/** Get the current FPS.
	@return The number of drawn frames in the last second.
	@remark The FPS is only updated once each second.
**/
int CEngine::GetFPS()
{
	return m_iCurrentFPS;
}

SDL_Surface* CEngine::LoadImage( std::string filename )
{
    SDL_Surface* loadedImage = NULL;
    SDL_Surface* optimizedImage = NULL;
    loadedImage = IMG_Load( filename.c_str() );
    
    if( loadedImage != NULL )
    {
    	if( loadedImage->format->Amask ) {
    		optimizedImage = SDL_DisplayFormatAlpha( loadedImage );
		} else {
			optimizedImage = SDL_DisplayFormat( loadedImage );
		}
        SDL_FreeSurface( loadedImage );
    }

    return optimizedImage;
}

SDL_Rect CEngine::GetTileClip( SDL_Surface* source, int slices, int rows = 1 )
{
        SDL_Rect temp_rect;

        // Find tile size,
        temp_rect.x = ( ( source->w )/slices );
        temp_rect.y = ( ( source->h) * (rows - 1) );
        temp_rect.w = (source->w)/slices;
        temp_rect.h = (source->h)/rows;

        return temp_rect;
}

void CEngine::ApplySurface( int x, int y, SDL_Surface* source, SDL_Surface* destination, SDL_Rect *clip = NULL )
{
    SDL_Rect offset;

	offset.x = x;
	offset.y = y;

    SDL_BlitSurface( source, clip, destination, &offset );
}
